/* ----------------------------------------------------------------------------
* File: api_command_dispatcher_impl.cpp
*
* Desc: Dispatcher for command messages implementation
*
* Created: 06/09/2011, by Alexey Korshunov
* ---------------------------------------------------------------------------- */
#include "api_command_dispatcher_impl.h"
#include "api_event.h"

#define IID_COMMAND_EVENT_SINK					1
#define WAIT_TIME_COMMAND_DISPATCHER			0		// ms
#define WAIT_TIME_ATTACH_COMMAND_DISPATCHER		200		// ms

namespace SkypeProtection
{
	namespace
	{
		class CommandEventSink
			: public IDispEventImpl<IID_COMMAND_EVENT_SINK, CommandEventSink, &__uuidof(SKYPE4COMLib::_ISkypeEvents), &SKYPE4COMLib::LIBID_SKYPE4COMLib, 1, 0>
		{
		public:
			explicit CommandEventSink(APICommandHandler* handler)
				: m_CommandHandler(handler)
			{
			}

			// Event dispatching
			void __stdcall MessageStatus(SKYPE4COMLib::IChatMessage* pMessage, SKYPE4COMLib::TChatMessageStatus Status)
			{
				if (!m_CommandHandler)
					m_CommandHandler->OnMessageStatus(pMessage, Status);
			}

			BEGIN_SINK_MAP(CommandEventSink)
				SINK_ENTRY_INFO(IID_COMMAND_EVENT_SINK, __uuidof(SKYPE4COMLib::_ISkypeEvents), 1, &MessageStatus,  &MessageStatusInfo)
			END_SINK_MAP()

		private:
			APICommandHandler* m_CommandHandler;
		};
	}

	APICommandDispatcherImpl::APICommandDispatcherImpl(const APICommandDispatcherParams &params)
		: APIProxy(true)
	{
		m_AttachHandler.reset(params.AttachHandler);
		m_CommandHandler.reset(params.CommandHandler);

		if (Start() != Common::SKYPE_SUCCESS)
			throw Common::Exception("APICommandDispatcher: ERROR - Unable to start dispatcher");
	}

	APICommandDispatcherImpl::~APICommandDispatcherImpl()
	{
		Stop();
	}

	Common::Error APICommandDispatcherImpl::RunLogic(SKYPE4COMLib::ISkype* skype)
	{
		std::auto_ptr<CommandEventSink> eventSink(new CommandEventSink(m_CommandHandler.get()));

		if (StartProxyProcess(skype, eventSink.get()) != Common::SKYPE_SUCCESS)
			return Common::SKYPE_ERROR;

		MSG msg;
		while(!QueryExitStatus(WAIT_TIME_COMMAND_DISPATCHER) && GetMessage(&msg, NULL, 0, 0 ))
		{
			TranslateMessage(&msg);
			DispatchMessage(&msg);
		}

		StopProxyProcess(skype, eventSink.get());
		return Common::SKYPE_SUCCESS;
	}

	Common::Error APICommandDispatcherImpl::StartProxyProcess(SKYPE4COMLib::ISkype* skype, CommandEventSink* sink)
	{
		m_Logger->Print("APICommandDispatcher::StartProxyProcess() - start");

		// Advise event listener
		HRESULT hr = sink->DispEventAdvise(skype);
		if (hr != S_OK)
		{
			m_Logger->Print("APICommandDispatcher: ERROR - Unable to advise event listener");
			throw Common::Exception("APICommandDispatcher: ERROR - Unable to advise event listener");
		}
		m_Logger->Print("APICommandDispatcher: advised event listener (OK)");

		// Start client
		if (skype->GetClient()->IsRunning == VARIANT_FALSE)
			skype->GetClient()->Start(VARIANT_TRUE, VARIANT_FALSE);

		// Connect to Skype API without waiting
		skype->Attach(6, VARIANT_FALSE);

		// Wait API permission
		SKYPE4COMLib::TAttachmentStatus attachStatus = skype->GetAttachmentStatus();
		while (attachStatus != SKYPE4COMLib::TAttachmentStatus::apiAttachSuccess)
		{
			if (attachStatus == SKYPE4COMLib::TAttachmentStatus::apiAttachRefused)
			{
				sink->DispEventUnadvise(skype);
				m_Logger->Print("APICommandDispatcher: attach refused (FAILED)");
				throw Common::Exception("Object was refused");
			}
			else
			{
				// notify about attaching process
				if (m_AttachHandler.get())
					m_AttachHandler->OnAttaching();
			}

			Sleep(WAIT_TIME_ATTACH_COMMAND_DISPATCHER);
			attachStatus = skype->GetAttachmentStatus();
		}

		m_Logger->Print("APICommandDispatcher: attached (OK)");
		return Common::SKYPE_SUCCESS;
	}

	Common::Error APICommandDispatcherImpl::StopProxyProcess(SKYPE4COMLib::ISkype* skype, CommandEventSink* sink)
	{
		sink->DispEventUnadvise(skype);
		return Common::SKYPE_SUCCESS;
	}

	std::auto_ptr<APICommandDispatcher> CreateAPICommandDispatcher(const APICommandDispatcherParams& params)
	{
		std::auto_ptr<APICommandDispatcher> dispatcher;
		try
		{
			std::auto_ptr<APICommandDispatcherImpl> obj(new APICommandDispatcherImpl(params));
			dispatcher = obj;
		}
		catch (...)
		{
		}
		return dispatcher;
	}
}